<?php
/*
 * Cache library. Supports XCache, eAccelerator, APC, Memcached and native implementation.
 *
 * @author Demedes Vadim
 * @site http://demedes.com
 * @copyright 2009 (c) Demedes Vadim
 * @license GPL
 *
 */


class Cache {

	var $driver='memcached';
	var $path;
	var $memcached;
	var $memcached_server;
	var $memcached_port;
	var $cache_timeout;
	var $connect;
	
	function Cache()
	{
		$this->CI =& get_instance();
		$this->CI->config->load('cache');
		$this->driver = $this->CI->config->item('cache_driver');
		$this->path = $this->CI->config->item('cache_path');
		$m = $this->CI->config->item('cache_memcached');
		$this->memcached_server = $m[0];
		$this->memcached_port = $m[1];
		$this->cache_timeout = $this->CI->config->item('cache_timeout');
		$connect = $this->driver($this->driver, $this->memcached_server, $this->memcached_port);
		//$this->CI->load->helper('file');
	}
	
	/*
	 * Setting driver for the library
	 *
	 * @return bool
	 * @param driver
	 * @param server(only for memcached)
	 * @param port(only for memcached)
	 * @access public
	 *
	 */
	function driver($d, $server=0, $port=0)
	{
		return true;
		switch($d)
		{
			case 'fs':
				$this->CI =& get_instance();
				$this->CI->load->helper('file');
				$this->driver='fs';
				break;
			case 'memcached':
				if($this->is_loaded('memcache'))
				{
					$this->memcached=new Memcache();
					$connect = @$this->memcached->connect($this->memcached_server, $this->memcached_port);
					$this->driver='memcached';
					return TRUE;
				}
				return FALSE;
			case 'apc':
				if($this->is_loaded('apc'))
				{
					$this->driver='apc';
					return TRUE;
				}
				return FALSE;
			case 'xcache':
				if($this->is_loaded('xcache'))
				{
					$this->driver='xcache';
					return TRUE;
				}
				return FALSE;
			case 'eaccelerator':
				if($this->is_loaded('eaccelerator'))
				{
					$this->driver='eaccelerator';
					return TRUE;
				}
				return FALSE;
			default:
				$this->driver='fs';
				break;
		}
		return TRUE;
	}
	
	/*
	 * Checking for a driver
	 * 
	 * @param driver
	 * @access public
	 * @return bool
	 *
	 */
	function is_loaded($d)
	{
		if(extension_loaded($d))
		{
			return TRUE;
		}
		return FALSE;
	} 
	
	/*
	 * Storing a value
	 *
	 * @param key
	 * @param value
	 * @param timer
	 * @access public
	 * @return bool
	 *
	 */
	function set($key, $value, $time=null)
	{
		if (is_null($time)){
			$time = $this->cache_timeout;
		}
		switch($this->driver)
		{
			case 'fs':
				write_file($this->path.md5($key), $value);
				return TRUE;
			case 'xcache':
				return xcache_set($key, $value, $time);
			case 'memcached':
				if ($this->connect){
					return @$this->memcached->set($key, $value, $time);	
				}
				return false;
			case 'apc':
				return apc_add($key, $value, $time);
			case 'eaccelerator':
				return eaccelerator_put($key, $value, $time);
			default:
				break;
		}
	}
	
	/*
	 * Retrieveing a value
	 *
	 * @param key
	 * @access public
	 * @return mixed
	 *
	 */
	function get($key)
	{
		switch($this->driver)
		{
			case 'fs':
				return read_file($this->path.md5($key));
			case 'xcache':
				return xcache_get($key);
			case 'memcached':
				if ($this->connect){
					return @$this->memcached->get($key);
				}else{
					return null;
				}
			case 'apc':
				return apc_fetch($key);
			case 'eaccelerator':
				return eaccelerator_get($key);
			default:
				break;
		}
	}
	
	/*
	 * Checking for a data(if exists)
	 *
	 * @param key
	 * @access public
	 * @return bool
	 *
	 */
	function exists($key)
	{
		switch($this->driver)
		{
			case 'fs':
				if(file_exists($this->path.md5($key)))
				{
					return TRUE;
				}
				return FALSE;
			case 'xcache':
				return xcache_isset($key);
			case 'memcached':
				if(! $this->memcached->get($key))
				{
					return FALSE;
				}
				return TRUE;
			case 'apc':
				apc_fetch($key, $flag);
				if(! $flag)
				{
					return FALSE;
				}
				return TRUE;
			case 'eaccelerator':
				//TODO
				break;
			default:
				break;
		}
	}
	
	/*
	 * Deleting data
	 *
	 * @param key
	 * @access public
	 * @return bool
	 *
	 */
	function delete($key)
	{
		switch($this->driver)
		{
			case 'fs':
				return unlink($this->path.md5($key));
			case 'xcache':
				return xcache_unset($key);
			case 'memcached':
				return $this->memcached->delete($key);
			case 'apc':
				return apc_delete($key);
			case 'eaccelerator':
				eaccelerator_put($key, '', 0);
				break;
			default:
				break;
		}
	}
	
	/*
	 * Increment
	 *
	 * @param key
	 * @access public
	 * @return int
	 *
	 */
	function increment($key)
	{
		switch($this->driver)
		{
			case 'fs':
				//TODO
				break;
			case 'xcache':
				return xcache_inc($key);
			case 'memcached':
				return $this->memcached->increment($key);
			case 'apc':
				//TODO
				break;
			case 'eaccelerator':
				//TODO
				break;
			default:
				break;
		}
	}
	
	/*
	 * Decrement
	 *
	 * @param key
	 * @access public
	 * @return int
	 *
	 */
	function decrement($key)
	{
		switch($this->driver)
		{
			case 'fs':
				// TODO
				break;
			case 'xcache':
				return xcache_dec($key);
			case 'memcached':
				return $this->memcached->decrement($key);
			case 'apc':
				//TODO
				break;
			case 'eaccelerator':
				//TODO
				break;
			default:
				break;
		}
	}
	
	/*
	 * Cleaning
	 *
	 * @access public
	 * @return void
	 *
	 */
	function clean()
	{
		switch($this->driver)
		{
			case 'fs':
				delete_files($this->path);
				break;
			case 'xcache':
				//TODO
				break;
			case 'memcached':
				return $this->memcached->flush();
			case 'apc':
				return apc_clear_cache();
			case 'eaccelerator':
				return eaccelerator_clear();
			default:
				break;
		}
	}
	
/**
	 * Call a library's cached result or create new cache
	 *
	 * @access	public
	 * @param	string
	 * @return	array
	 */
    public function library($library, $method, $arguments = array(), $expires = null)
    {
    	return $this->call($library, $method, $arguments, $expires);
    }
    
	/**
	 * Call a model's cached result or create new cache
	 *
	 * @access	public
	 * @return	array
	 */
    public function model($model, $method, $arguments = array(), $expires = null)
    {
    	return $this->call($model, $method, $arguments, $expires);
    }
    
    // Depreciated, use model() or library()
    private function call($property, $method, $arguments = array(), $expires = null)
    {    	
   		if (is_null($expires)){
			$expires = $this->cache_timeout;
		}
    	// Clean given arguments to a 0-index array
		$arguments = array_values($arguments);
		
    	$cache_key = $property.$method.serialize($arguments);	
		// See if we have this cached
		$cached_responce = @$this->get($cache_key);

		// Not FALSE? Return it
		if($cached_responce)
        {
        	return $cached_responce;
        }
        
        else
        {
        	// Call the model or library with the method provided and the same arguments
        	$new_responce = call_user_func_array(array($this->CI->$property, $method), $arguments);
        	$this->set($cache_key, $new_responce, $expires);
        	return $new_responce;
        }
    }
	
}

/* End of file: Cache.php */